#pragma once

#include <unordered_map>
#include <functional>
#include <cassert>
#include "err.hpp"
#include "log.hpp"
#include "sock.hpp"
#include "util.hpp"
#include "epoller.hpp"
#include "protocol.hpp"

namespace tcpserver
{
    // 类声明
    class TcpServer;

    static const int defaultport = 8080;
    static const int num = 128;

    class Connection // 一个套接字一个Connection，每个套接字都要自己独立的inBuffer、outBuffer
    {
        using func_t = std::function<void(Connection *)>;

    public:
        Connection(int sock, TcpServer *pTcp)
            : _sock(sock),
              _pTcp(pTcp) {}
        ~Connection() {}

    public:
        void Register(func_t recver, func_t sender, func_t excepter) // 注册事件
        {
            _recver = recver;
            _sender = sender;
            _excepter = excepter;
        }
        void Close()
        {
            close(_sock);
        }

    public:
        int _sock;
        std::string _inBuffer;  // 输入缓冲区
        std::string _outBuffer; // 输出缓冲区
        func_t _recver;         // 从sock读
        func_t _sender;         // 向sock写
        func_t _excepter;       // 处理sock的异常事件
        TcpServer *_pTcp;       // 用来调用EnableReadWrite接口
        uint64_t _lastTime;     // 最新访问时间，每次读、写都更新时间
    };
    class TcpServer
    {
        using func_t = std::function<void(Connection *)>;

    private:
        uint16_t _port;
        Sock _sock; // Sock只管_listensock
        Epoller _epoll;
        std::unordered_map<int, Connection *> _connections; // [fd, Connection*]
        int _num;                                           // 一次获取就绪事件的最大个数
        struct epoll_event *_revents;                       // 获取就绪事件和对应fd的结构体数组
        func_t _service;
        // hander_t hander;// 用来解析自己定制的协议

    public:
        TcpServer(func_t func, uint16_t port = defaultport)
            : _service(func),
              _port(port),
              _revents(nullptr)
        {
        }
        ~TcpServer()
        {
            // unordered_map的析构会自动先调用，但存的Connection*指针不会正确释放，可以使用智能指针
            _sock.Close();
            _epoll.Close();
            if (nullptr == _revents)
                delete[] _revents;
        }

    public:
        void InitServer()
        {
            // 创建listensock
            _sock.Socket();
            _sock.Bind(_port);
            _sock.Listen();
            // 创建epoll模型
            _epoll.Create();
            // 将listensock添加到epoll和哈希表
            // bind在这里的作用是绑定成员函数Accept，返回一个新的可调用对象用来满足func_t类型
            // _n是新的可调用对象的第n个参数

            // std::function<void(TcpServer*, Connection*)> fn = Accept;// 类内成员函数第一个参数是this
            // std::bind(&TcpServer::Accept, this, std::placeholders::_1) 意思是
            // 第一个参数this绑定好了，新的可调用对象只需要传一个参数
            AddConnection(_sock.GetlistenSock(), EPOLLIN | EPOLLET,
                          std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
            _revents = new struct epoll_event[num];
            _num = num;
        }
        void Dispatcher()
        {
            const int timeout = 1000; // 1000ms
            while (1)
            {
                Loop(timeout);
                // logMessage(DEBUG, "time out");
                // 可以遍历所有哈希表，比较最新通信时间和当前时间，太久没动服务器就主动断开连接
            }
        }

        void EnableReadWrite(Connection *conn, bool readable, bool writeable) // 改变关心的事件
        {
            uint32_t events = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
            _epoll.Control(conn->_sock, events, EPOLL_CTL_MOD);
        }

    private:
        bool IsConnectionExists(int sock)
        {
            auto iter = _connections.find(sock);
            return iter != _connections.end();
        }
        void print()
        {
            for (auto &it : _connections)
            {
                std::cout << it.first << ": " << it.second << std::endl;
            }
        }
        void Loop(int timeout)
        {
            int n = _epoll.Wait(_revents, _num, timeout);
            // logMessage(DEBUG, "epoll wait success");
            for (int i = 0; i < n; ++i)
            {
                int sock = _revents[i].data.fd;
                uint32_t events = _revents[i].events;

                // 将所有的异常问题，全部转化 成为读写问题
                if (events & EPOLLERR)
                    events |= (EPOLLIN | EPOLLOUT);
                if (events & EPOLLHUP)
                    events |= (EPOLLIN | EPOLLOUT);
                // 读事件就绪
                // print();
                if ((events & EPOLLIN) && IsConnectionExists(sock))
                {
                    // logMessage(DEBUG, "EPOLLIN IsConnectionExists");
                    if (_connections[sock]->_recver)
                        _connections[sock]->_recver(_connections[sock]);
                }

                if ((events & EPOLLOUT) && IsConnectionExists(sock)) // 写事件就绪
                {
                    // logMessage(DEBUG, "EPOLLOUT IsConnectionExists");
                    if (_connections[sock]->_sender)
                        _connections[sock]->_sender(_connections[sock]);
                }
            }
        }
        void Accepter(Connection *conn)
        {
            if (conn == nullptr)
                return;
            // logMessage(DEBUG, "Accepter begin");
            // listensock是非阻塞的
            // ET模式只通知一次，要一次把连接全部读完
            while (1)
            {
                std::string clientip;
                uint16_t clientport;
                int err = 0;
                int sock = _sock.Accept(&clientip, &clientport, &err);
                if (sock >= 0)
                {
                    AddConnection(sock, EPOLLIN | EPOLLET,
                                  std::bind(&TcpServer::Reader, this, std::placeholders::_1),
                                  std::bind(&TcpServer::Writer, this, std::placeholders::_1),
                                  std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
                    logMessage(DEBUG, "git a new link, info: [%s:%d]", clientip.c_str(), clientport);
                }
                else
                {
                    if (err == EAGAIN || err == EWOULDBLOCK)
                        break; // 读取完
                    else if (err == EINTR)
                        continue; // 被中断
                    else
                        break; // 真正的错误
                }
            }
            // logMessage(DEBUG, "Accepter end");
        }
        void Reader(Connection *conn)
        {
            if (conn == nullptr)
                return;
            // logMessage(DEBUG, "Reader begin");
            conn->_lastTime = time(nullptr);
            char buffer[1024];
            while (1)
            {

                ssize_t s = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s] = 0;
                    conn->_inBuffer += buffer;
                    logMessage(NORMAL, "-----recv: %s", conn->_inBuffer.c_str());
                    _service(conn); // 回调cal处理
                }
                else if (s == 0) // 对端关闭连接
                {
                    if (conn && conn->_excepter) // 判断是否注册了事件，避免空指针调用
                    {
                        // logMessage(DEBUG, "Reader except s == 0");
                        logMessage(NORMAL, "client quit");
                        conn->_excepter(conn);
                        return;
                    }
                }
                else
                {
                    // logMessage(DEBUG, "Reader errno: %d", errno);
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        if (conn && conn->_excepter)
                        {
                            // logMessage(DEBUG, "Reader -- sock: %d, Reader except s < 0", conn->_sock);
                            conn->_excepter(conn);
                            return;
                        }
                    }
                }
            }
            // logMessage(DEBUG, "Reader end");
        }
        void Writer(Connection *conn)
        {
            if (conn == nullptr)
                return;
            // logMessage(DEBUG, "Writer begin");
            conn->_lastTime = time(nullptr);

            while (1)
            {

                ssize_t s = send(conn->_sock, conn->_outBuffer.c_str(), conn->_outBuffer.size(), 0);
                // logMessage(DEBUG, "send %d size", s);
                if (s >= 0)
                {
                    if (conn->_outBuffer.empty()) // 发完了
                    {
                        // logMessage(DEBUG, "send finish, outBuffer empty");
                        // EnableReadWrite(conn, true, false); // 写事件写完了，不关心写事件了
                        break;
                    }
                    else
                    {
                        // logMessage(DEBUG, "outBuffer erase before: %s size: %d",
                                //    conn->_outBuffer.c_str(), conn->_outBuffer.size());
                        conn->_outBuffer.erase(0, s);
                        // logMessage(DEBUG, "outBuffer erase after: %s size: %d",
                                //    conn->_outBuffer.c_str(), conn->_outBuffer.size());
                    }
                }
                else
                {
                    // logMessage(DEBUG, "Write errno: %d", errno);
                    if (errno == EAGAIN || errno == EWOULDBLOCK) // 缓冲区写满了
                        break;
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        if (conn && conn->_excepter) // 处理异常错误
                        {
                            // logMessage(DEBUG, "Writer except");
                            conn->_excepter(conn);
                            return;
                        }
                    }
                }
            } // while
            if (!conn->_outBuffer.empty())
            {
                conn->_pTcp->EnableReadWrite(conn, true, true); // 没写完继续关心写事件，事件就绪又会调用Writer
            }
            else
            {
                conn->_pTcp->EnableReadWrite(conn, true, false);
            }
            // logMessage(DEBUG, "Writer end");
        }
        void Excepter(Connection *conn)
        {
            if (conn == nullptr)
                return;
            logMessage(DEBUG, "Excepter begin");
            // 从epoll模型删除
            _epoll.Control(conn->_sock, 0, EPOLL_CTL_DEL); // 直接从红黑树中删除sock，不用传event
            // 从哈希表中删除
            // erase不会帮你释放Connection*，可以用RAII，释放节点的时会调shared_ptr的析构释放Connection*
            _connections.erase(conn->_sock);
            // 关闭fd
            conn->Close();
            // 释放conn -- conn是new出来的记得释放
            logMessage(DEBUG, "关闭 %d 的所有资源", conn->_sock);
            delete conn;
        }

        void AddConnection(int sock, uint32_t events, func_t readFunc, func_t writeFunc, func_t errFunc)
        {
            // 1. 为sock创建Connection对象
            if (events & EPOLLET)
            {
                util::SetNonBlock(sock); // ET模式fd要设置非阻塞
            }

            Connection *conn = new Connection(sock, this);
            // 注册事件
            conn->Register(readFunc, writeFunc, errFunc);

            // 添加到epoll和哈希表中
            bool r = _epoll.AddEvent(sock, events);
            assert(r == 1);
            _connections.insert({sock, conn});
            logMessage(NORMAL, "add sock %d in epoll and unordered_map", sock);
        }
    };
}